bitkeeper revision 1.1108.2.24 (410133delTf8MZfF-gDO2KEJpDxCOg)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Fri, 23 Jul 2004 15:50:54 +0000 (15:50 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Fri, 23 Jul 2004 15:50:54 +0000 (15:50 +0000)
Ensure that merged scatter-gather lists are valid in the face
of moulinexed machine address space.

linux-2.4.26-xen-sparse/include/asm-xen/pci.h

index 74ae5ba8b134ffdacd79dda9a676db2dccf5685c..382b7a41dec9d8ab59eb5f387772d49f67fe70cb 100644 (file)
@@ -145,7 +145,8 @@ static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
 static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
                             int nents, int direction)
 {
-       int i;
+       int i, j, nr_pfns;
+       unsigned long first_pfn;
 
        if (direction == PCI_DMA_NONE)
                out_of_line_bug();
@@ -159,10 +160,28 @@ static inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
                else if (!sg[i].address && !sg[i].page)
                        out_of_line_bug();
  
-               if (sg[i].address)
+               if (sg[i].address) {
                        sg[i].dma_address = virt_to_bus(sg[i].address);
-               else
+                       first_pfn = virt_to_phys(sg[i].address) >> PAGE_SHIFT;
+                       nr_pfns = (((unsigned long)sg[i].address & 
+                           (PAGE_SIZE-1)) + sg[i].length + PAGE_SIZE - 1) >>
+                           PAGE_SHIFT;
+               } else {
                        sg[i].dma_address = page_to_bus(sg[i].page) + sg[i].offset;
+                       first_pfn = page_to_phys(sg[i].page) >> PAGE_SHIFT;
+                       nr_pfns = (sg[i].offset + sg[i].length + PAGE_SIZE - 
+                           1) >> PAGE_SHIFT;
+               }
+
+                /*
+                 * Check that we merged physical buffers are also contiguous
+                 * in machine-address space. We try to fail by returning 0.
+                 */
+                for (j = 1; j < nr_pfns; j++) {
+                    if ( unlikely(pfn_to_mfn(first_pfn+j) != 
+                                  (pfn_to_mfn(first_pfn)+j)) )
+                        return 0;
+                }
        }
  
        flush_write_buffers();